package com.zanglikun.apacheBeanUtil;

/**
 * @author : zanglikun
 * @date : 2025/10/31 13:58
 * @desc : Copyright © zanglikun.com
 */
import com.github.pagehelper.PageInfo;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 通用 Bean 拷贝工具（基于 Apache Commons BeanUtils）。
 *
 * 相关包均来自：org.apache.commons。但是额外引用了PageHelper的包
 * 功能:
 * - 单对象/列表浅拷贝
 * - 忽略 null 字段复制
 * - 字段名映射复制 (sourceName -> targetName)
 * - 分页 PageInfo 泛型转换
 * - 深拷贝(需外部序列化函数)
 * - Bean <-> Map
 * - 差异比较 & 合并
 */
public final class BeanCopyUtilWithPageHelper {

    private static final BeanUtilsBean BEAN_UTILS_BEAN;

    static {
        ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();
        BEAN_UTILS_BEAN = new BeanUtilsBean(convertUtilsBean);
    }

    private BeanCopyUtilWithPageHelper() {
    }

    /* ---------- 单对象复制 ---------- */

    /**
     * 浅拷贝单个对象
     *
     * @param source 源对象
     * @param targetClass 目标类型
     * @return 拷贝后的目标对象
     */
    public static <S, T> T copy(S source, Class<T> targetClass) {
        if (source == null) return null;
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            BEAN_UTILS_BEAN.copyProperties(target, source);
            return target;
        } catch (Exception e) {
            throw new IllegalStateException("copy failed: " + e.getMessage(), e);
        }
    }

    /**
     * 浅拷贝，指定忽略字段
     *
     * @param source 源对象
     * @param target 目标对象
     * @param ignoreFields 要忽略的字段名
     */
    public static <S, T> void copy(S source, T target, String... ignoreFields) {
        if (source == null || target == null) return;
        Set<String> ignore = ignoreFields == null ? Collections.emptySet() : new HashSet<>(Arrays.asList(ignoreFields));
        try {
            PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(source.getClass());
            for (PropertyDescriptor desc : descriptors) {
                String name = desc.getName();
                if ("class".equals(name) || ignore.contains(name)) continue;

                if (PropertyUtils.isReadable(source, name) && PropertyUtils.isWriteable(target, name)) {
                    try {
                        Object value = PropertyUtils.getProperty(source, name);
                        PropertyUtils.setProperty(target, name, value);
                    } catch (Exception ignored) {
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException("copy failed: " + e.getMessage(), e);
        }
    }

    /**
     * 忽略 null 值的拷贝
     *
     * @param source 源对象
     * @param target 目标对象
     * @param ignoreFields 要忽略的字段名
     */
    public static <S, T> void copyIgnoreNull(S source, T target, String... ignoreFields) {
        if (source == null || target == null) return;
        Set<String> ignore = ignoreFields == null ? Collections.emptySet() : new HashSet<>(Arrays.asList(ignoreFields));
        try {
            PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(source.getClass());
            for (PropertyDescriptor desc : descriptors) {
                String name = desc.getName();
                if ("class".equals(name) || ignore.contains(name)) continue;

                if (PropertyUtils.isReadable(source, name) && PropertyUtils.isWriteable(target, name)) {
                    try {
                        Object value = PropertyUtils.getProperty(source, name);
                        if (value != null) {
                            PropertyUtils.setProperty(target, name, value);
                        }
                    } catch (Exception ignored) {
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException("copyIgnoreNull failed: " + e.getMessage(), e);
        }
    }

    /* ---------- 字段映射复制 ---------- */

    /**
     * 带字段映射的拷贝（源字段名 -> 目标字段名）
     *
     * @param source 源对象
     * @param targetClass 目标类型
     * @param fieldMapping 字段映射关系 (sourceName -> targetName)
     * @param ignoreNullSource 是否忽略源对象中的 null 值
     * @return 拷贝后的目标对象
     */
    public static <S, T> T copyWithFieldMapping(S source,
                                                Class<T> targetClass,
                                                Map<String, String> fieldMapping,
                                                boolean ignoreNullSource) {
        if (source == null) return null;
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            // 先进行普通拷贝
            BEAN_UTILS_BEAN.copyProperties(target, source);

            // 再处理字段映射
            if (fieldMapping != null && !fieldMapping.isEmpty()) {
                for (Map.Entry<String, String> e : fieldMapping.entrySet()) {
                    String sName = e.getKey();
                    String tName = e.getValue();
                    if (StringUtils.isBlank(sName) || StringUtils.isBlank(tName)) continue;

                    if (PropertyUtils.isReadable(source, sName) && PropertyUtils.isWriteable(target, tName)) {
                        Object val = PropertyUtils.getProperty(source, sName);
                        if (ignoreNullSource && val == null) continue;
                        PropertyUtils.setProperty(target, tName, val);
                    }
                }
            }
            return target;
        } catch (Exception e) {
            throw new IllegalStateException("copyWithFieldMapping failed: " + e.getMessage(), e);
        }
    }

    /* ---------- 列表复制 ---------- */

    /**
     * 拷贝集合
     *
     * @param src 源集合
     * @param targetClass 目标类型
     * @return 拷贝后的目标集合
     */
    public static <S, T> List<T> copyList(Collection<S> src, Class<T> targetClass) {
        if (src == null || src.isEmpty()) return Collections.emptyList();
        return src.stream().map(s -> copy(s, targetClass)).collect(Collectors.toList());
    }

    /**
     * 使用 Supplier 拷贝集合
     *
     * @param src 源集合
     * @param supplier 目标对象供应器
     * @return 拷贝后的目标集合
     */
    public static <S, T> List<T> copyList(Collection<S> src, Supplier<T> supplier) {
        if (src == null || src.isEmpty()) return Collections.emptyList();
        return src.stream().map(s -> {
            T t = supplier.get();
            copy(s, t);
            return t;
        }).collect(Collectors.toList());
    }

    /**
     * 忽略 null 值的集合拷贝
     *
     * @param src 源集合
     * @param supplier 目标对象供应器
     * @return 拷贝后的目标集合
     */
    public static <S, T> List<T> copyListIgnoreNull(Collection<S> src, Supplier<T> supplier) {
        if (src == null || src.isEmpty()) return Collections.emptyList();
        return src.stream().map(s -> {
            T t = supplier.get();
            copyIgnoreNull(s, t);
            return t;
        }).collect(Collectors.toList());
    }

    /**
     * 带字段映射的集合拷贝
     *
     * @param src 源集合
     * @param targetClass 目标类型
     * @param fieldMapping 字段映射关系
     * @param ignoreNullSource 是否忽略源对象中的 null 值
     * @return 拷贝后的目标集合
     */
    public static <S, T> List<T> copyListWithFieldMapping(Collection<S> src,
                                                          Class<T> targetClass,
                                                          Map<String, String> fieldMapping,
                                                          boolean ignoreNullSource) {
        if (src == null || src.isEmpty()) return Collections.emptyList();
        return src.stream()
                .map(s -> copyWithFieldMapping(s, targetClass, fieldMapping, ignoreNullSource))
                .collect(Collectors.toList());
    }


    /* ---------- 深拷贝(序列化/反序列化) ---------- */

    /**
     * 深拷贝，需外部提供序列化和反序列化函数
     *
     * @param source 源对象
     * @param serializer 序列化函数
     * @param deserializer 反序列化函数
     * @return 深拷贝后的对象
     */
    public static <T> T deepCopy(T source,
                                 Function<T, String> serializer,
                                 Function<String, T> deserializer) {
        if (source == null) return null;
        String data = serializer.apply(source);
        return deserializer.apply(data);
    }

    /* ---------- 差异检测 ---------- */

    /**
     * 比较两个对象的差异
     *
     * @param a 对象A
     * @param b 对象B
     * @return 差异映射 (字段名 -> [值A, 值B])
     */
    public static <S, T> Map<String, Object[]> diff(S a, T b) {
        Map<String, Object[]> result = new LinkedHashMap<>();
        if (a == null || b == null) return result;
        try {
            Map<String, Object> aDesc = PropertyUtils.describe(a);
            Map<String, Object> bDesc = PropertyUtils.describe(b);
            for (String key : aDesc.keySet()) {
                if ("class".equals(key)) continue;
                Object v1 = aDesc.get(key);
                Object v2 = bDesc.get(key);
                if (!Objects.equals(v1, v2)) {
                    result.put(key, new Object[]{v1, v2});
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException("diff failed: " + e.getMessage(), e);
        }
        return result;
    }

    /* ---------- 合并: override 非 null 覆盖 base ---------- */

    /**
     * 合并两个对象，override 的非 null 值覆盖 base
     *
     * @param base 基础对象
     * @param override 覆盖对象
     * @return 合并后的对象（修改了base）
     */
    public static <T> T mergePreferNonNull(T base, T override) {
        if (base == null) return override;
        if (override == null) return base;
        try {
            PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(base.getClass());
            for (PropertyDescriptor desc : descriptors) {
                String name = desc.getName();
                if ("class".equals(name)) continue;

                if (PropertyUtils.isReadable(override, name) && PropertyUtils.isWriteable(base, name)) {
                    try {
                        Object ov = PropertyUtils.getProperty(override, name);
                        if (ov != null) {
                            PropertyUtils.setProperty(base, name, ov);
                        }
                    } catch (Exception ignored) {
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException("mergePreferNonNull failed: " + e.getMessage(), e);
        }
        return base;
    }

    /* ---------- 安全取值 ---------- */

    /**
     * 取两个对象中第一个非 null 的
     *
     * @param a 对象A
     * @param b 对象B
     * @return 第一个非 null 对象
     */
    public static <T> T firstNonNull(T a, T b) {
        return ObjectUtils.firstNonNull(a, b);
    }

    /* ---------- Bean -> Map ---------- */

    /**
     * Bean 转 Map
     *
     * @param bean Bean 对象
     * @return 属性映射
     */
    public static Map<String, Object> toMap(Object bean) {
        if (bean == null) return Collections.emptyMap();
        try {
            Map<String, Object> map = PropertyUtils.describe(bean);
            map.remove("class");
            return map;
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new IllegalStateException("toMap failed: " + e.getMessage(), e);
        }
    }

    /* ---------- Map -> Bean ---------- */

    /**
     * Map 转 Bean
     *
     * @param map 属性映射
     * @param type Bean 类型
     * @return Bean 对象
     */
    public static <T> T fromMap(Map<String, ?> map, Class<T> type) {
        if (map == null) return null;
        try {
            T t = type.getDeclaredConstructor().newInstance();
            BEAN_UTILS_BEAN.populate(t, map);
            return t;
        } catch (Exception e) {
            throw new IllegalStateException("fromMap failed: " + e.getMessage(), e);
        }
    }



    /* ---------- PageInfo 泛型转换 ---------- */

    /**
     * 转换 PageInfo 对象的泛型类型
     *
     * @param source 源 PageInfo
     * @param targetClass 目标元素类型
     * @return 转换后的 PageInfo
     */
    public static <S, T> PageInfo<T> copyPageInfo(PageInfo<S> source, Class<T> targetClass) {
        if (source == null) return null;
        PageInfo<T> target = new PageInfo<>();
        List<S> srcList = source.getList();
        List<T> mapped = (srcList == null ? Collections.emptyList()
                : srcList.stream().map(s -> copy(s, targetClass)).collect(Collectors.toList()));
        fillPageMeta(source, target, mapped);
        return target;
    }

    /**
     * 转换 PageInfo 对象，支持增强函数
     *
     * @param source 源 PageInfo
     * @param targetClass 目标元素类型
     * @param enhancer 增强函数 (sourceItem, targetItem) -> void
     * @return 转换后的 PageInfo
     */
    public static <S, T> PageInfo<T> copyPageInfo(PageInfo<S> source,
                                                  Class<T> targetClass,
                                                  BiConsumer<S, T> enhancer) {
        if (source == null) return null;
        PageInfo<T> target = new PageInfo<>();
        List<T> mapped = new ArrayList<>();
        List<S> srcList = source.getList();
        if (srcList != null) {
            for (S s : srcList) {
                T t = copy(s, targetClass);
                if (enhancer != null) {
                    try {
                        enhancer.accept(s, t);
                    } catch (Exception ignored) {
                    }
                }
                mapped.add(t);
            }
        }
        fillPageMeta(source, target, mapped);
        return target;
    }

    private static <S, T> void fillPageMeta(PageInfo<S> source, PageInfo<T> target, List<T> list) {
        target.setList(list);
        target.setTotal(source.getTotal());
        target.setPageNum(source.getPageNum());
        target.setPageSize(source.getPageSize());
        target.setPages(source.getPages());
        target.setSize(list.size());
        target.setStartRow(source.getStartRow());
        target.setEndRow(source.getEndRow());
        target.setPrePage(source.getPrePage());
        target.setNextPage(source.getNextPage());
        target.setIsFirstPage(source.isIsFirstPage());
        target.setIsLastPage(source.isIsLastPage());
        target.setHasPreviousPage(source.isHasPreviousPage());
        target.setHasNextPage(source.isHasNextPage());
        target.setNavigatePages(source.getNavigatePages());
        target.setNavigatepageNums(source.getNavigatepageNums());
        target.setNavigateFirstPage(source.getNavigateFirstPage());
        target.setNavigateLastPage(source.getNavigateLastPage());
    }

}

